package com.lau.config;

import lombok.Setter;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 线程池配置
 *
 * @author Lau
 **/
@Configuration
@EnableAsync
@ConfigurationProperties(prefix = "lau.task.pool")
@Setter
public class ThreadPoolConfig {

    // 核心线程池大小

    private int corePoolSize;

    // 最大可创建的线程数
    private int maxPoolSize;

    // 队列最大长度
    private int queueCapacity;

    // 线程池维护线程所允许的空闲时间
    private int keepAliveSeconds;
    /**
     * ThreadPoolTaskExecutor的处理流程
     * 当池子大小小于corePoolSize，就新建线程，并处理请求
     * 当池子大小等于corePoolSize，把请求放入workQueue中，池子里的空闲线程就去workQueue中取任务并处理
     * 当workQueue放不下任务时，就新建线程放入线程池，并处理请求，如果池子大小撑到了maximumPoolSize，就用RejectedExecutionHandler来做拒绝处理
     * 当池子的线程数大于corePoolSize时，多余的线程会等待keepAliveTime长时间，如果无请求可处理就自行销毁
     *
     */
    @Bean(name = "threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor tp = new ThreadPoolTaskExecutor();
        //设置核心线程数
        tp.setCorePoolSize(corePoolSize);
        //设置最大线程数
        tp.setMaxPoolSize(maxPoolSize);
        //线程使用的缓冲队列
        tp.setQueueCapacity(queueCapacity);
        //设置程序关闭时要等待线程全部执行完
        tp.setWaitForTasksToCompleteOnShutdown(true);
        //设置等待时间，超过等待时间后立即停止
        tp.setAwaitTerminationSeconds(keepAliveSeconds);
        //线程名称前缀
        tp.setThreadNamePrefix("Async-");
        /* 当线程的任务缓存队列已满并且线程池中的线程数量已经达到了最大连接数，如果还有任务来就会采取拒绝策略，
         * 通常有四种策略：
         *ThreadPoolExecutor.AbortPolicy：丢弃任务并抛出异常：RejectedExcutionException异常
         *ThreadPoolExecutor.DiscardPolicy：丢弃任务，但是不抛出异常
         *ThreadPoolExecutor.DiscardOldestPolicy: 丢弃队列最前面的任务，然后重新尝试执行任务（重复此过程）
         *ThreadPoolExecutor.CallerRunsPolicy：重试添加当前的任务，自动重复调用execute()方法，直到成功。
         *ThreadPoolExecutor. 扩展重试3次，如果3次都不成功在移除。
         *jmeter 压力测试 1s=500
         * */
        tp.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //初始化线程
        tp.initialize();
        return tp;
    }

    /**
     * 执行周期性或定时任务
     * 通过ScheduledExecutorService执行的异步任务，最多只能executorcorePoolSize个线程同时执行，不会新增
     */
    @Bean(name = "scheduledExecutorService")
    protected ScheduledExecutorService scheduledExecutorService() {
        return new ScheduledThreadPoolExecutor(corePoolSize,
                new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
                new ThreadPoolExecutor.CallerRunsPolicy()) {
            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                super.afterExecute(r, t);
            }
        };
    }
}
